﻿<ComClass(ClsVector3d.ClassId, ClsVector3d.InterfaceId, ClsVector3d.EventsId)>
Public Class ClsVector3d

#Region "COM GUID"
    ' 这些 GUID 提供此类的 COM 标识 
    ' 及其 COM 接口。若更改它们，则现有的
    ' 客户端将不再能访问此类。
    Public Const ClassId As String = "33b04561-be20-442c-9d14-00544e641991"
    Public Const InterfaceId As String = "6d5a767e-5284-4849-8e94-335f22012995"
    Public Const EventsId As String = "60e1f1ae-bbbf-45ce-ab9b-3513002f7843"
#End Region

    ' 可创建的 COM 类必须具有一个不带参数的 Public Sub New() 
    ' 否则， 将不会在 
    ' COM 注册表中注册此类，且无法通过
    ' CreateObject 创建此类。
    Public Sub New()
        MyBase.New()
    End Sub

#Region "字段和属性"
    Private _xValue As Double
    Private _yValue As Double
    Private _zValue As Double
    Public ReadOnly Property Origin As ClsVector3d
        Get
            Return New ClsVector3d() With {.X = 0, .Y = 0, .Z = 0}
        End Get
    End Property
    Public Property X As Double
        Get
            Return _xValue
        End Get
        Set(value As Double)
            _xValue = value
        End Set
    End Property
    ''' <summary>
    ''' 纵坐标
    ''' </summary>
    ''' <returns></returns>
    Public Property Y As Double
        Get
            Return _yValue
        End Get
        Set(value As Double)
            _yValue = value
        End Set
    End Property
    Public Property Z As Double
        Get
            Return _zValue
        End Get
        Set(ByVal value As Double)
            _zValue = value
        End Set
    End Property
    Public ReadOnly Property Length As Double
        Get
            Return Math.Sqrt((((Me.X * Me.X) + (Me.Y * Me.Y)) + (Me.Z * Me.Z)))
        End Get
    End Property

    Public ReadOnly Property UnitVector As ClsVector3d
        Get
            Return Me.DivideBy(Me.Length)
        End Get
    End Property
    Public ReadOnly Property NegateVector As ClsVector3d
        Get
            Return Me.Multiply(-1)
        End Get
    End Property

    Public ReadOnly Property XAxis As ClsVector3d
        Get
            Return New ClsVector3d With {.X = 1, .Y = 0, .Z = 0}
        End Get
    End Property
    Public ReadOnly Property YAxis As ClsVector3d
        Get
            Return New ClsVector3d With {.X = 0, .Y = 1, .Z = 0}
        End Get
    End Property

    Public ReadOnly Property ZAxis As ClsVector3d
        Get
            Return New ClsVector3d With {.X = 0, .Y = 0, .Z = 1}
        End Get
    End Property


    Public ReadOnly Property IsUnitVector3d As Boolean
        Get
            Return Math.Abs(Me.Length - 1.0) <= 0.00000001
        End Get
    End Property

    ''' <summary>
    ''' 转字符串格式
    ''' </summary>
    ''' <returns></returns>
    Public ReadOnly Property StringStyle As String
        Get
            Return $"({Me.X},{Me.Y},{Me.Z})"
        End Get
    End Property




#End Region

#Region "四则运算"
    Public Function Multiply(ByVal number As Double) As ClsVector3d
        Return New ClsVector3d With {
        .X = (Me.X * number),
        .Y = (Me.Y * number),
        .Z = (Me.Z * number)
    }
    End Function

    Public Function DivideBy(ByVal number As Double) As ClsVector3d
        Return New ClsVector3d With {
        .X = (Me.X / number),
        .Y = (Me.Y / number),
        .Z = (Me.Z / number)
    }
    End Function


    Public Function Add(ByVal v As ClsVector3d) As ClsVector3d
        Return New ClsVector3d With {
        .X = (Me.X + v.X),
        .Y = (Me.Y + v.Y),
        .Z = (Me.Z + v.Z)
    }
    End Function
    Public Function SubStact(ByVal v As ClsVector3d) As ClsVector3d
        Return New ClsVector3d With {
        .X = (Me.X - v.X),
        .Y = (Me.Y - v.Y),
        .Z = (Me.Z - v.Z)
    }
    End Function

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="v2"></param>
    ''' <returns></returns>
    Public Function CrossProductWith(ByVal v2 As ClsVector3d) As ClsVector3d
        Return New ClsVector3d With {
        .X = ((Me.Y * v2.Z) - (Me.Z * v2.Y)),
        .Y = -((Me.X * v2.Z) - (Me.Z * v2.X)),
        .Z = ((Me.X * v2.Y) - (Me.Y * v2.X))
    }
    End Function

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="v2"></param>
    ''' <returns></returns>
    Public Function DotProductWith(ByVal v2 As ClsVector3d) As Double
        Return (((Me.X * v2.X) + (Me.Y * v2.Y)) + (Me.Z * v2.Z))
    End Function


    ''' <summary>
    ''' 当前向量与给定向量构成平面的法向量
    ''' </summary>
    ''' <param name="v2">给定向量</param>
    ''' <returns>当前向量与给定向量构成平面的法向量</returns>
    Public Function GetNormalVector(ByVal v2 As ClsVector3d) As ClsVector3d
        Return Me.CrossProductWith(v2).UnitVector
    End Function

    Public Function IsEqualTo(ByVal b As ClsVector3d) As Boolean
        Dim m As New ClsMath
        Return m.IsEqual(Me.X, b.X) AndAlso m.IsEqual(Me.Y, b.Y) AndAlso m.IsEqual(Me.Z, b.Z)
    End Function

    Public Function ProjectionOnVector(ByVal v1 As ClsVector3d) As ClsVector3d
        Return v1.UnitVector.Multiply(Me.DotProductWith(v1.UnitVector))
    End Function

#End Region


#Region "方法"
    ''' <summary>
    ''' 当前向量与给定向量构成平面的夹角
    ''' </summary>
    ''' <param name="v"></param>
    ''' <returns></returns>
    Public Function GetAngleTo(ByVal v As ClsVector3d) As Double
        Dim cos As Double = (Me.DotProductWith(v) / (Me.Length * v.Length))
        If (cos >= 1) Then
            Return 0
        End If
        If (cos <= -1) Then
            Return Math.PI
        End If
        Return Math.Acos(cos)
    End Function


    Public Function ParallelTo(ByVal v As ClsVector3d, ByVal threshold As Double) As Boolean
        Dim cross = Me.CrossProductWith(v)
        Dim MathHelper As New ClsMath
        If Not MathHelper.IsZero(cross.X, threshold) Then
            Return False
        End If
        If Not MathHelper.IsZero(cross.Y, threshold) Then
            Return False
        End If
        If Not MathHelper.IsZero(cross.Z, threshold) Then
            Return False
        End If
        Return True
    End Function

    Public Function PerpendicularTo(ByVal v As ClsVector3d, ByVal threshold As Double) As Boolean
        Dim MathHelper As New ClsMath
        Return MathHelper.IsZero(Me.DotProductWith(v), threshold)
    End Function

    Public Function ToArray() As Double()
        Return {Me.X, Me.Y, Me.Z}
    End Function
#End Region

    ''' <summary>
    ''' 构造函数
    ''' </summary>
    ''' <param name="arr">2个元素的double</param>
    Public Sub CreatorByVariant(arr As Object)
        Me.X = arr(LBound(arr))
        Me.Y = arr(LBound(arr) + 1)
        Me.Z = arr(UBound(arr))
    End Sub
    Public Sub CreateByXyz(_x As Double, _y As Double, _z As Double)
        Me.X = _x : Me.Y = _y : Me.Z = _z
    End Sub
End Class


